package ru.ifmo.croak.impl.replication.team1.io;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.ifmo.croak.api.Message;
import ru.ifmo.croak.impl.replication.team1.LocalContext;

import java.io.*;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.util.UUID;

public class MulticastMessage {
    private static final Logger log = LoggerFactory.getLogger(MulticastMessage.class);

    public enum MessageType {
        NORMAL,
        PING,
        UPDATE_REQUEST
    }

    private DatagramPacket datagramPacket;
    private Message message;
    private InetSocketAddress tcpSocketAddress;
    private MessageType messageType;
    private UUID sourceUUID;

    public MulticastMessage(DatagramPacket datagramPacket) throws BrokenMessageException {
        this.datagramPacket = datagramPacket;
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(datagramPacket.getData(), datagramPacket.getOffset(), datagramPacket.getLength());
            ObjectInput dataInput = new ObjectInputStream(byteArrayInputStream);
            messageType = (MessageType) dataInput.readObject();
            sourceUUID = (UUID) dataInput.readObject();
            int tcpPort = dataInput.readInt();
            tcpSocketAddress = new InetSocketAddress(datagramPacket.getAddress(), tcpPort);
            if (messageType == MessageType.NORMAL) {
                message = new Message();
                message.setId((UUID) dataInput.readObject());
                message.setTime(dataInput.readLong());
                message.setAuthor(dataInput.readUTF());
                message.setText(dataInput.readUTF());
            }
        } catch (IOException e) {
            throw new BrokenMessageException(e);
        } catch (ClassNotFoundException e) {
            throw new BrokenMessageException(e);
        } catch (ClassCastException e) {
            throw new BrokenMessageException(e);
        }
    }

    public MulticastMessage(Message message, int tcpPort, InetSocketAddress multicastAddress, LocalContext localContext) {
        this(MessageType.NORMAL, message, tcpPort, multicastAddress, localContext);
    }

    public MulticastMessage(MessageType messageType, Message message, int tcpPort, InetSocketAddress multicastAddress, LocalContext localContext) {
        this.message = message;
        this.messageType = messageType;
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutput dataOutput = new ObjectOutputStream(byteArrayOutputStream);
            dataOutput.writeObject(messageType);
            sourceUUID = localContext.getLocalUUID();
            dataOutput.writeObject(localContext.getLocalUUID());
            dataOutput.writeInt(tcpPort);
            if (messageType == MessageType.NORMAL) {
                dataOutput.writeObject(message.getId());
                dataOutput.writeLong(message.getTime());
                dataOutput.writeUTF(message.getAuthor());
                dataOutput.writeUTF(message.getText());
            }
            dataOutput.close();
            datagramPacket = new DatagramPacket(byteArrayOutputStream.toByteArray(), byteArrayOutputStream.size(), multicastAddress.getAddress(), multicastAddress.getPort());
        } catch (IOException e) {
            log.error("Something bad happens ;)", e);
        }
    }

    public DatagramPacket getDatagramPacket() {
        return datagramPacket;
    }

    public Message getMessage() {
        return message;
    }

    public InetSocketAddress getTcpSocketAddress() {
        return tcpSocketAddress;
    }

    public MessageType getMessageType() {
        return messageType;
    }

    public UUID getSourceUUID() {
        return sourceUUID;
    }
}
